using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TableTennisHitTargets : TableTennisGameTypeBase
{
	[Serializable]
	public class HitTargetsDefinition
	{
		[Serializable]
		public class TargetDefinition
		{
			public FloatRange width = new FloatRange
			{
				min = 0.25f,
				max = 0.25f
			};

			public FloatRange height = new FloatRange
			{
				min = 1f,
				max = 1f
			};

			public FloatRange x = new FloatRange
			{
				min = 0f,
				max = 0f
			};

			public FloatRange y = new FloatRange
			{
				min = 0f,
				max = 0f
			};

			public int hitsBeforeContinue = 20;

			public string text;

			public bool shouldMove;

			public bool chooseXFromList;

			public List<float> xList = new List<float>();

			public FloatRange xSpeedRange;

			public FloatRange ySpeedRange;

			public FloatRange xMovementRange;

			public FloatRange yMovementRange;

			public bool alternateTopBackSpin;

			public float chanceForBackspin;

			public FloatRange topHeight;

			public FloatRange bottomHeight;

			public TargetDefinition Clone()
			{
				TargetDefinition targetDefinition = new TargetDefinition();
				targetDefinition.width = width;
				targetDefinition.height = height;
				targetDefinition.x = x;
				targetDefinition.y = y;
				targetDefinition.hitsBeforeContinue = hitsBeforeContinue;
				targetDefinition.text = text;
				targetDefinition.shouldMove = shouldMove;
				targetDefinition.chooseXFromList = chooseXFromList;
				targetDefinition.xList = new List<float>();
				targetDefinition.xList.AddRange(xList);
				targetDefinition.xSpeedRange = xSpeedRange;
				targetDefinition.ySpeedRange = ySpeedRange;
				targetDefinition.xMovementRange = xMovementRange;
				targetDefinition.yMovementRange = yMovementRange;
				targetDefinition.alternateTopBackSpin = alternateTopBackSpin;
				targetDefinition.chanceForBackspin = chanceForBackspin;
				targetDefinition.topHeight = topHeight;
				targetDefinition.bottomHeight = bottomHeight;
				return targetDefinition;
			}
		}

		public FloatRange xRange;

		public FloatRange zRange;

		public bool chooseXFromList;

		public List<float> xList = new List<float>();

		public float heightAboveNet = 0.5f;

		public float strength = 0.75f;

		public float delay = 1.5f;

		public int ballsAvailable;

		public bool isAttackPointsRelative = true;

		public float attackPoints = -0.5f;

		public HitParamsDB.HitType hitType;

		public bool useMinQuality;

		public float minQuality;

		public List<TargetDefinition> targets = new List<TargetDefinition>();

		public HitTargetsDefinition Clone()
		{
			HitTargetsDefinition hitTargetsDefinition = new HitTargetsDefinition();
			hitTargetsDefinition.xRange = xRange;
			hitTargetsDefinition.zRange = zRange;
			hitTargetsDefinition.chooseXFromList = chooseXFromList;
			hitTargetsDefinition.xList = new List<float>();
			hitTargetsDefinition.xList.AddRange(xList);
			hitTargetsDefinition.heightAboveNet = heightAboveNet;
			hitTargetsDefinition.strength = strength;
			hitTargetsDefinition.delay = delay;
			hitTargetsDefinition.ballsAvailable = ballsAvailable;
			hitTargetsDefinition.isAttackPointsRelative = isAttackPointsRelative;
			hitTargetsDefinition.attackPoints = attackPoints;
			hitTargetsDefinition.hitType = hitType;
			hitTargetsDefinition.targets = new List<TargetDefinition>();
			foreach (TargetDefinition target in targets)
			{
				hitTargetsDefinition.targets.Add(target.Clone());
			}
			return hitTargetsDefinition;
		}
	}

	public class ScoreState
	{
		public int hitTargets;

		public int ballsFired;
	}

	public struct CurrentTargetState
	{
		public int targetIndex;

		public int hits;
	}

	private MatchUI ui;

	private TableTennisPlayerBase humanPlayer;

	private MatchParameters.MatchParams matchParams;

	private ShotParams currentShot;

	private FlightInitialConditionsSearch search = new FlightInitialConditionsSearch();

	private MagnusForceAndAirResistanceSquaredBallFlight symFlight = new MagnusForceAndAirResistanceSquaredBallFlight();

	protected List<PhysicsBall> balls = new List<PhysicsBall>();

	protected int currentBallIndex;

	public HitTargetsDefinition hitTargetsDefinition = new HitTargetsDefinition();

	protected ScoreState scoreState = new ScoreState();

	protected CurrentTargetState currentTargetState;

	private TabletopHighlight.HighlightDefinition highlightDefinition;

	private bool highlightHit;

	protected Vector3 highlightHitPos;

	public int targetsHit
	{
		get
		{
			return scoreState.hitTargets;
		}
	}

	public BallMachineController ballMachine { get; protected set; }

	private Vector3 ballPosition
	{
		get
		{
			return new Vector3(0f, Table.tabletopy + 0.5f, Table.halfLength * 1.5f);
		}
	}

	protected HitTargetsDefinition.TargetDefinition GetCurrentTarget()
	{
		if (currentTargetState.targetIndex >= hitTargetsDefinition.targets.Count)
		{
			return null;
		}
		return hitTargetsDefinition.targets[currentTargetState.targetIndex];
	}

	public override void Init(TableTennisMatchController match, MatchUI ui, MatchParameters.MatchParams matchParams)
	{
		ballMachine = match.starter.CreateBallMachine();
		match.ui.tabletopHighlight.HideHighlight();
		this.matchParams = matchParams;
		base.match = match;
		this.ui = ui;
		humanPlayer = match.CreateHumanPlayer(-1, true);
		if (matchParams.hitTargetsDefinition != null)
		{
			hitTargetsDefinition = matchParams.hitTargetsDefinition;
		}
		OnCameraBehaviourDone();
		balls.Add(match.ball);
		int num = 4;
		while (balls.Count < num)
		{
			balls.Add(match.CreateBall());
		}
		if (hitTargetsDefinition.targets.Count == 0)
		{
			HitTargetsDefinition.TargetDefinition item = new HitTargetsDefinition.TargetDefinition();
			hitTargetsDefinition.targets.Add(item);
		}
		UITools.SetActive(match.ui.singlePlayerUI, false);
		if (match.ui.hitsDisplay != null)
		{
			match.ui.hitsDisplay.Show(0, hitTargetsDefinition.ballsAvailable, hitTargetsDefinition.ballsAvailable);
		}
		if (matchParams.eventsDelegate != null)
		{
			matchParams.eventsDelegate.StartMatch(match);
		}
	}

	private void OnCameraBehaviourDone()
	{
		isMatchReady = true;
	}

	public override void StartMatch(int activePlayer = 0)
	{
		highlightDefinition = new TabletopHighlight.HighlightDefinition();
		Intro();
	}

	private void Intro()
	{
		StartCoroutine(DoIntro());
	}

	private IEnumerator DoIntro()
	{
		ballMachine.Show();
		ballMachine.GetUp();
		yield return new WaitForSeconds(2f);
		ballMachine.PointAt(Vector3.up * (Table.tabletopy + 1f));
		SetNewRandomTarget();
		StartCoroutine(DoCheckTargets());
		FireBallEvery();
	}

	private void SetNewRandomTarget()
	{
		HitTargetsDefinition.TargetDefinition currentTarget = GetCurrentTarget();
		float value = currentTarget.x.Random() * Table.halfwidth;
		float value2 = currentTarget.y.Random() * Table.halfLength;
		highlightDefinition.height = currentTarget.height.Random() * Table.halfLength;
		highlightDefinition.width = currentTarget.width.Random() * Table.halfwidth;
		if (currentTarget.alternateTopBackSpin)
		{
			bool decissionWithSuccess = GaussDistribution.instance.getDecissionWithSuccess(currentTarget.chanceForBackspin);
			value2 = (float)((!decissionWithSuccess) ? 1 : 0) * Table.halfLength;
			highlightDefinition.height = ((!decissionWithSuccess) ? currentTarget.topHeight.Random() : currentTarget.bottomHeight.Random()) * Table.halfLength;
		}
		if (currentTarget.chooseXFromList)
		{
			value = currentTarget.xList[UnityEngine.Random.Range(0, currentTarget.xList.Count) % currentTarget.xList.Count] * Table.halfwidth;
		}
		value = Mathf.Clamp(value, 0f - Table.halfwidth + highlightDefinition.realWidth * 0.5f, Table.halfwidth - highlightDefinition.realWidth * 0.5f);
		value2 = Mathf.Clamp(value2, highlightDefinition.realHeight * 0.5f, Table.halfLength - highlightDefinition.realHeight * 0.5f);
		highlightDefinition.positionOnTable = new Vector3(value, value2);
		highlightDefinition.shouldMove = currentTarget.shouldMove;
		highlightDefinition.xMoveRange = currentTarget.xMovementRange;
		highlightDefinition.yMoveRange = currentTarget.yMovementRange;
		highlightDefinition.speed = new Vector2(currentTarget.xSpeedRange.Random(), currentTarget.ySpeedRange.Random());
		highlightDefinition.text = currentTarget.text;
		if (hitTargetsDefinition.hitType == HitParamsDB.HitType.Smash)
		{
			TableTennisHumanPlayer tableTennisHumanPlayer = humanPlayer as TableTennisHumanPlayer;
			if (tableTennisHumanPlayer != null)
			{
				tableTennisHumanPlayer.PrepareForSmash();
			}
		}
		ui.tabletopHighlight.ShowHighlight(highlightDefinition, Color.yellow);
	}

	public override void OnMissedServe()
	{
	}

	private void ShootBall(PhysicsBall ball, float xPos)
	{
		match.SetCurrentBall(ball);
		Vector3 position = match.ball.position;
		float num = hitTargetsDefinition.zRange.Random();
		Vector3 vector = Vector3.forward * humanPlayer.tableSide * Table.halfLength * num + Vector3.right * xPos * Table.halfwidth;
		Vector3 forwardDirection = (vector - ballPosition).OnGround();
		forwardDirection.Normalize();
		float angle = FlightInitialConditionsSearch.GetAngle(hitTargetsDefinition.heightAboveNet, 0.02f, position);
		int num2 = 0;
		ShootBall(position, forwardDirection, angle, num2, hitTargetsDefinition.strength);
	}

	public IEnumerator DoCheckTargets()
	{
		while (true)
		{
			if (highlightHit)
			{
				scoreState.hitTargets++;
				currentTargetState.hits++;
				PlayerCareerStageDefinition.GameParameters gameParams = MatchParameters.InitParameters.gameParams;
				int prevStars = 0;
				int stars = 0;
				float starProgression = 0f;
				if (gameParams != null)
				{
					prevStars = gameParams.GetStarCountWithZero(scoreState.hitTargets - 1);
					stars = gameParams.GetStarCountWithZero(scoreState.hitTargets);
					starProgression = gameParams.GetStarCountProgress(scoreState.hitTargets);
				}
				HitTargetsDefinition.TargetDefinition currentTarget = GetCurrentTarget();
				if (currentTargetState.hits >= currentTarget.hitsBeforeContinue && currentTargetState.targetIndex < hitTargetsDefinition.targets.Count - 1)
				{
					currentTargetState.targetIndex++;
					currentTargetState.hits = 0;
				}
				if (match.ui.hitsDisplay != null)
				{
					match.ui.hitsDisplay.UpdateScore(scoreState.hitTargets, highlightHitPos, stars > prevStars, starProgression);
				}
				yield return new WaitForSeconds(0.5f);
				SetNewRandomTarget();
				highlightHit = false;
			}
			yield return null;
		}
	}

	public void ShootBall(Vector3 position, Vector3 forwardDirection, float angle, float angularVelocityMagnitude, float strength)
	{
		search.SetupWithHumanRanges(forwardDirection, position, symFlight);
		FlightInitialConditionsSearch.MaxResult maxResult = search.FindMaxAcceptableVelocity(angle, angularVelocityMagnitude, FlightInitialConditionsSearch.OptimumSearchType.SearchMin);
		FlightInitialConditionsSearch.MaxResult maxResult2 = search.FindMaxAcceptableVelocity(angle, angularVelocityMagnitude, FlightInitialConditionsSearch.OptimumSearchType.SearchMax);
		float velocityMagnitude = Mathf.Lerp(maxResult.magnitude, maxResult2.magnitude, strength);
		FlightParams initialConditions = search.GetInitialConditions(velocityMagnitude, angle, angularVelocityMagnitude);
		FlightSimulator instance = Singleton<FlightSimulator>.instance;
		FlightSimulator.SequenceSimulationParams sim = FlightSimulator.SequenceSimulationParams.DefaultRecordFlightParams(initialConditions, symFlight);
		PhysicsBall ball = match.ball;
		instance.FillFlightSequence(ball.flightSequence, sim);
		ball.flightSequence.Reset();
		ball.hitParams.attackPoints = hitTargetsDefinition.attackPoints;
		if (hitTargetsDefinition.isAttackPointsRelative)
		{
			ball.hitParams.attackPoints += GGPlayerSettings.instance.statsManager.playerStats.GetSkillValue(1);
		}
		ball.hitParams.opponentDefensePoints = 0f;
		ball.isBallInGame = true;
		ball.hitParams.reductionFactor = 1f;
		Color white = Color.white;
		white.a = 0f;
		match.ui.ballFollower.SetColor(white);
		ball.playerThatHitBall = null;
		ball.transform.parent = null;
		humanPlayer.ReceiveBall(ball);
	}

	private void FireBallEvery()
	{
		StartCoroutine(DoFireBallEvery());
	}

	private IEnumerator DoFireBallEvery()
	{
		float delay = hitTargetsDefinition.delay;
		int listIndex = 0;
		do
		{
			yield return new WaitForSeconds(delay * 0.5f);
			PhysicsBall ball = balls[currentBallIndex % balls.Count];
			float xPos = hitTargetsDefinition.xRange.Random();
			if (hitTargetsDefinition.chooseXFromList && hitTargetsDefinition.xList.Count > 0)
			{
				xPos = hitTargetsDefinition.xList[listIndex % hitTargetsDefinition.xList.Count] * Table.halfwidth;
				listIndex++;
			}
			currentBallIndex++;
			ballMachine.MountBall(ball);
			ballMachine.PointAt(Vector3.up * (Table.tabletopy + 1f) + Vector3.right * xPos * Table.halfwidth);
			yield return new WaitForSeconds(delay * 0.5f);
			scoreState.ballsFired++;
			if (match.ui.hitsDisplay != null)
			{
				match.ui.hitsDisplay.UpdateBalls(hitTargetsDefinition.ballsAvailable - scoreState.ballsFired, hitTargetsDefinition.ballsAvailable);
			}
			match.ui.ballFollower.Init(ball);
			ShootBall(ball, xPos);
		}
		while (scoreState.ballsFired < hitTargetsDefinition.ballsAvailable || hitTargetsDefinition.ballsAvailable < 0);
		yield return new WaitForSeconds(2f);
		OnGameDone();
	}

	public void ResumeGameWithBalls(int ballsAvailable)
	{
		scoreState.ballsFired -= ballsAvailable;
		match.ui.hitsDisplay.UpdateBalls(hitTargetsDefinition.ballsAvailable - scoreState.ballsFired, hitTargetsDefinition.ballsAvailable);
		StopAllCoroutines();
		Intro();
	}

	private void OnGameDone()
	{
		if (matchParams.eventsDelegate != null)
		{
			matchParams.eventsDelegate.MatchComplete(match);
		}
	}

	public override void OnPlayerHitBall(TableTennisPlayerBase player, HitBallParamsPhysicsFlight hitBallParams)
	{
		if (match.ball.isBallInGame || match.ball.isServeBall)
		{
			base.numShotsInPoint++;
			match.ball.isBallInGame = true;
			currentShot = new ShotParams();
			currentShot.isServeShot = match.ball.isServeBall;
		}
	}

	public override void OnBallHitObject(PhysicsBall ball, Vector3 pos, Vector3 vel, HitObjectTag hitObjectTag)
	{
		if (!ball.isBallInGame)
		{
			return;
		}
		if (hitObjectTag == HitObjectTag.Floor)
		{
			ball.isBallInGame = true;
		}
		else
		{
			if (!(ball.playerThatHitBall == humanPlayer))
			{
				return;
			}
			if (hitObjectTag == HitObjectTag.Table)
			{
				Vector3 position = pos;
				position.y = Table.tabletopy + 0.02f;
				if (!highlightHit && match.ui.tabletopHighlight.IsHighlightHit(pos))
				{
					if (ball.hitParams.quality < matchParams.hitTargetsDefinition.minQuality && matchParams.hitTargetsDefinition.useMinQuality)
					{
						ui.ballMarker.ShowOnPos(position, Color.red, 0.25f);
						ui.tabletopHighlight.FlashColor(Color.yellow, Color.red, 0.5f);
					}
					else
					{
						ui.ballMarker.ShowOnPos(position, Color.green, 0.25f);
						ui.tabletopHighlight.SetHighlightHit(Color.green);
						highlightHitPos = pos;
						highlightHit = true;
					}
				}
				else
				{
					ui.ballMarker.ShowOnPos(position, Color.red, 0.5f);
				}
			}
			ball.isBallInGame = false;
		}
	}
}
